PROJECT = "stoicheia"

math.randomseed(os.time())

function dump(value, output)
	if not output then output = print end
	local function dumpRec(depth, name, value, dumped)
		local indent = string.rep("  ", depth)
		local prefix = indent
		if name then
			if type(name) == "number" then
				prefix = prefix .. "[" .. name .. "] = "
			else
				prefix = prefix .. name .. " = "
			end
		end
		if type(value) == "nil" then
			output(prefix .. "nil")
			return
		end
		if type(value) == "table" then
			if dumped[value] then
				output(prefix .. "-ALREADY-DUMPED-")
			else
				dumped[value] = true
				output(prefix .. "{")
				for n, v in pairs(value) do
					dumpRec(depth + 1, n, v, dumped)
				end
				output(indent .. "}")
			end
			return
		end
		if type(value) == "string" then
			value = string.format("%q", value)
		else
			value = tostring(value)
		end
		output(string.format("%s%s,", prefix, value))
	end
	dumpRec(0, nil, value, {})
end

function try(catch, f, ...)
	local t = { pcall(f, ...) }
	if not t[1] then
		catch(t[2])
		return false
	else
		return unpack(t)
	end
end

string.startsWith = function(str, prefix)
	assert(type(str) == "string")
	assert(type(prefix) == "string")
	if #prefix > #str then return false end
	local strprefix = str:sub(1, #prefix)
	return strprefix == prefix
end

string.endsWith = function(str, postfix)
	assert(type(str) == "string")
	assert(type(postfix) == "string")
	if #postfix > #str then return false end
	local strpostfix = str:sub(#str - #postfix + 1)
	return strpostfix == postfix
end

string.trim = function(str)
	str = string.gsub(str, "^[ \t\r\n]*", "")
	str = string.gsub(str, "[ \t\r\n]*$", "")
	return str
end

string.splititer = function(str, sep)
	if str == nil then return nil end
	assert(type(str) == "string", "str must be a string")
	assert(type(sep) == "string", "sep must be a string")
	return string.gmatch(str, "[^\\" .. sep .. "]+")
end

string.split = function(str, sep)
	local ret = {}
	for seg in string.splititer(str, sep) do
		ret[#ret + 1] = seg
	end
	return ret
end

string.indexof = function(str, substr)
	return string.find(str, substr, 1, true)
end

string.utf8len = function(str)
	local bytes = { string.byte(str, 1, #str) }
	local count = 0
	for i, b in ipairs(bytes) do
		if b < 128 or b >= 128 + 64 then
			count = count + 1
		end
	end
	return count
end

table.join = function(t, sep)
	local ret
	for n, v in pairs(t) do
		local seg = tostring(v)
		if ret == nil then
			ret = seg
		else
			ret = ret .. sep .. seg
		end
	end
	return ret
end

table.equals = function(t1, t2)
	if t1 == nil then return t2 == nil end
	if t2 == nil then return false end
	assert(type(t1) == "table", "First argument must be a table")
	assert(type(t2) == "table", "Second argument must be a table")
	local function eq(a, b)
		for n, v in pairs(a) do
			local vb = b[n]
			if vb ~= v then return false end
		end
		return true
	end
	return eq(t1, t2) and eq(t2, t1)
end

common = {
	getUserPath = function(path)
		local home = os.getenv("HOME")
		local root = string.format("%s/.stoicheia", home)
		if path == nil then return root end
		return string.format("%s/%s", root, path)
	end,
	
	checkUserDir = function()
		local userConfig = common.getUserPath("config.lua")
		if not util.exists(userConfig) then
			log("common", "User config %q does not exist (first time run?), creating user stoicheia directory", userConfig)
			assert(util.mkdir(common.getUserPath()) == 0)
			assert(util.mkfifo(common.getUserPath("pipe")) == 0)
			assert(util.mkdir(common.getUserPath("tmp")) == 0)
			assert(util.mkdir(common.getUserPath("data")) == 0)
		end
	end,
	
	checkUserConfig = function()
		local userConfig = common.getUserPath("config.lua")
		if not util.exists(userConfig) then
			log("common", "User config %q does not exist (first time run?), copying template user config", userConfig)
			assert(util.cp(string.format("%s/config.lua", common.getConfig("path.templatedir")), userConfig))
		end
	end,
	
	getConfig = function(name)
		assert(type(name) == "string", "name must be string")
		local item = config
		for part in name:gmatch("[^.]+") do
			if type(item) ~= "table" then error("cannot index config by key `" .. part .."' in `" .. name .. "' - previous item is not table (" .. type(item) .. ")") end
			item = item[part]
		end
		return item
	end,
	
	getLanguage = function()
		local language = common.getConfig("language")
		if language ~= nil then return language end
		language = os.getenv("LANG")
		if language == nil then
			language = os.setlocale(nil, "ctype")
		end
		if language == nil then language = "C" end
		language = string.gmatch(language, "[^.]+")()
		return language
	end,
	
	getResource = function(name)
		local language = common.getLanguage()
		local values = resources[language]
		if values == nil then values = resources["C"] end
		local item = values
		for part in name:gmatch("[^.]+") do
			if type(item) ~= "table" then error("cannot index resource by key `" .. part .."' in `" .. name .. "' - previous item is not table (" .. type(item) .. ")") end
			if tonumber(part) ~= nil then
				part = tonumber(part)
			end
			item = item[part]
		end
		if item ~= nil then return item end
		return "*" .. name .. "*"
	end,
	
	absolutePath = function(path)
		if string.startsWith(path, "/") then return path end
		local segs = string.split(path, "/")
		if path[1] ~= "/" then
			local cwd = util.pwd()
			local cwdsegs = string.split(cwd, "/")
			for i, seg in pairs(segs) do
				table.insert(cwdsegs, seg)
			end
			segs = cwdsegs
		end
		local ret = {}
		for i = 1, #segs do
			local seg = segs[i]
			if seg ~= "." then
				if seg == ".." then
					table.remove(ret, #ret)
				else
					if seg == "~" then
						ret = {}
						table.insert(ret, os.getenv("HOME"))
					else
						table.insert(ret, seg)
					end
				end
			end
		end
		return "/" .. table.concat(ret, "/")
	end,
	
	tree = function(path, follow)
		local root = {}
		util.find(path, "f", follow, function(p)
			p = p:sub(#path + 1)
			if p:startsWith("/") then p = p:sub(2) end
			local node = root
			local segs = p:split("/")
			for i, seg in pairs(segs) do
				if i == #segs then
					node[seg] = true
				else
					if node[seg] == nil then node[seg] = {} end
					node = node[seg]
				end
			end
		end)
		return root
	end,
	
	traverse = function(tree, handler, path, filter, comp)
		if path == nil then
			path = ""
		else
			if not path:endsWith("/") then path = path .. "/" end
		end
		local function pass(path, dir)
			if filter == nil then return true end
			return filter(path, dir)
		end
		local function rec(node, name, depth, p)
			if not pass(path .. p, type(node) == "table") then return end
			if type(node) == "table" then
				local childcount, leafcount = 0, 0
				for n, v in pairs(node) do
					local dir = type(v) == "table"
					if pass(path .. p .. "/" .. n, dir) then
						if not dir then leafcount = leafcount + 1 end
						childcount = childcount + 1
					end
				end
				handler(node, name, depth, false, path .. p, leafcount, childcount)
				local keys = {}
				for k, v in pairs(node) do
					keys[#keys + 1] = k
				end
				table.sort(keys, comp)
				for i, key in pairs(keys) do
					local child = node[key]
					local cp = key
					if name ~= nil then cp = p .. "/" .. key end
					rec(child, key, depth + 1, cp)
				end
			else
				handler(node, name, depth, true, path .. p, nil, nil)
			end
		end
		rec(tree, nil, 0, "")
	end,
	
	suffixFilter = function(path, ...)
		local suffices = { ... }
		for i, suffix in pairs(suffices) do
			if path:endsWith("." .. suffix) then return true end
		end
		return false
	end,
	
	urlEncode = function(str)
		assert(type(str) == "string")
		local hex = "0123456789abcdef"
		str = str:gsub("[^%l%u%d]", function(c)
			c = c:byte(1)
			local hi = math.floor(c / 16)
			local lo = c % 16
			return string.format("%%%c%c", hex:byte(hi + 1), hex:byte(lo + 1))
		end)
		str = str:gsub(" ", "+")
		return str
	end,
	
	urlDecode = function(str)
		if str == nil then return nil end
		assert(type(str) == "string")
		local hex = "0123456789abcdef"
		str = str:gsub("+", " ")
		str = str:gsub("%%([0-9a-fA-F])([0-9a-fA-F])", function(his, los)
			local hi = hex:find(his:lower(), 1, true) - 1
			local lo = hex:find(los:lower(), 1, true) - 1
			return string.char((hi * 16) + lo)
		end)
		return str
	end,
	
	xmlEncode = function(str)
		str = string.gsub(str, "&", "&amp;")
		str = string.gsub(str, "<", "&lt;")
		str = string.gsub(str, ">", "&gt;")
		str = string.gsub(str, "\"", "&quot;")
		str = string.gsub(str, "'", "&apos;")
		return str
	end,
	
	xmlDecode = function(str)
		return string.gsub(str, "&(.-);", function(name)
			if name == "amp" then return "&" end
			if name == "quot" then return "\"" end
			if name == "apos" then return "'" end
			if name == "gt" then return ">" end
			if name == "lt" then return "<" end
		end)
	end
}

require("log")
require("util")
common.checkUserDir()
logopen()
require("config")
if DEVEL then
	config.path.cpath = "../jabber/?.so;../labeller/?.so"
	config.path.imagedir = "../image"
	config.path.svgdir = "../svg"
	config.path.templatedir = "../template"
	config.path.webgui = "../webgui/webgui"
	config.path.sounddir = "../sound"
end
common.checkUserConfig()
dofile(common.getUserPath("config.lua"))
require("resources")


